#include <htc.h>

//! Sets the pic configure bits, OSC=HS, WDT=OFF, PWRT=OFF, BOR=OFF, UNPROTECT=OFF
__CONFIG(HS & WDTDIS & PWRTDIS & BORDIS & UNPROTECT);

//! Oscillator frequency, this constant is used by __delay_ms()
#define _XTAL_FREQ	4000000
//! Constant definining the minimum servo pulse width
#define MIN_SERVO	1400
//! Constant definining the maximum servo pulse width
#define MAX_SERVO	2000
//! Constant defining servo pulse for an open hatch
#define HATCH_OPEN	MIN_SERVO
//! Constant defining servo pulse for a closed hatch
#define HATCH_CLOSED	MAX_SERVO
//! Constant definining the middle servo pulse width
#define MID_SERVO	( MAX_SERVO + MIN_SERVO ) / 2
//! pin used for detecting light
#define LIGHT_TRIGGER	RB4
//! pin used to show the uC is awake
#define AWAKE_LED	RB1

//! Structure used for the Real Time Clock
struct sRTC
{
	//! Member for storing # of seconds. [0-255]
	unsigned char seconds;
	//! Member for storing # of minutes. [0-255]
	unsigned char minutes;
	//! Member for storing # of hours. [0-255]
	unsigned char hours;
	//! Member for storing # of days. [0-255]
	unsigned char days;
};


//! variable for storing the servo pulse in micro seconds
volatile int servo = MIN_SERVO;
//! variable for keeping track of how long to hold the pulse
int pulse = 0;
//! keeps track of the frame #, there are 80 frames per 20ms
char frame = 1;
//! stores # of crickets to let out each day
unsigned char numCrickets = 5;
bit awake;

//! Array for displaying digits on a 7-segment display using PORTD
unsigned char segDigits[] = { 0b10000000, 0b11101100, 0b01000010, 0b01001000, 
			      0b00101100, 0b00011000, 0b00010000, 0b11001100,
	 		      0b00000000, 0b00001100, 0b00000100, 0b00110000,
	  		      0b10010010, 0b01100000, 0b00010010, 0b00010110 };


//! variable for keeping track of the RTC
volatile struct sRTC rtc;


void init( void );
void interrupt isrHandler( void );
void setServo( int pulseWidth );
void delay_500ms( void );


/**
 * Main function where all the action happens, not!
 *
 */
void main(void)
{
	unsigned char counter = 0;
	
	init();
	ei();
	
	if( !LIGHT_TRIGGER )
		setServo( HATCH_CLOSED );
	else
		setServo( HATCH_OPEN );

	delay_500ms();
	
	while(1)
	{
		if( !awake )
		{
			AWAKE_LED = 0;
			setServo( HATCH_CLOSED );
			PORTD = 0xFF;
			counter = (counter + 1) & 0x0F;
			__delay_ms(100);
			__delay_ms(100);
			__delay_ms(100);
#asm
			sleep
#endasm
		}
		else
		{
			AWAKE_LED = 1;
			PORTD = segDigits[counter];
			setServo( HATCH_OPEN );
//			delay_500ms();
		}
	}
}


/**
 * \brief Performs all the neccessary function calls to initialize the pic and the neccessary peripherals and pins
 *
 * <b>I/O Pins</b>:
 * RC2 is configured as an output for PWM
 *
 * <b>Peripherials that are initialized</b>:
 * CCP1 for PWM output
 *
 * <b>Enabled interrupts</b>:
 * \em Timer1 - set to trigger every 500ms
 * \em Timer2 - used for PWM output
 */
void init(void)
{
	INTCON	= 0b11000000;	// enable unmasked peripheral interrupts
	PIE1 	= 0b00000011;	// enable timer 1 and timer 2 interrupt
	RBIE	= 1;		// enable external interrupt on pin RB0
	INTEDG	= 1;
	TMR1H	= 0x0B;		// load 3036 into timer 1 to allow for an even
	TMR1L	= 0xDD;		// 500ms timer period
	
	TRISC	= 0b11111011;	// set RC2 as output for PWM
	CCP1CON	= 0b00001100;	// set CCP1 to PWM mode
	PR2 = 249;
	CCPR1L = 0;
	
	PORTD	= 0b00000000;
	TRISD 	= 0b00000000;
	PORTD	= segDigits[numCrickets];
	
	PORTB	= 0b00000000;
	TRISB	= 0b00010000;	// set RB4 as an input, rest of PORTB are outputs
	
	ADCON1	= 0b11111111;
	TRISA	= 0b11111111;	// set PORT A as inputs
	
	rtc.seconds	= 0;
	rtc.minutes	= 0;
	rtc.hours	= 0;
	
	T2CON 	= 0b00000100;	// enable timer 2, with prescaler set to 1:1
	T1CON	= 0b00110101;	// enable timer 1 with 1:8 prescaler
	
	AWAKE_LED = LIGHT_TRIGGER;
	awake = LIGHT_TRIGGER;
}


/*!
 * \brief Interrupt service routine.
 * 
 * \b Timer1 interrupts are handled and used to keep track of running time.
 * \b Timer2 interrupts for controlling the PWM duty cycle for servo control
 * Since timer2 is set to go off every 250us, PWM for the servo is split into
 * 80 frames for a total of 20ms.  PWM is adjusted to meet RC servo timings.
 */
void interrupt isrHandler(void)
{
	static bit secInc = 0;
	static bit lastPos = 0;
	if( TMR2IF )
	{
		TMR2IF = 0;		// clear interrupt flag
		
		if( --frame == 0 )
		{
			frame = 80;
			pulse = servo;
		}
		
		if( pulse > 250 )
		{
			CCPR1L = 250;
			pulse -= 250;
		}
		else
		{
			CCPR1L = pulse;
			pulse = 0;
		}
	}
	else if( TMR1IF )
	{
		TMR1H	= 0x0B;		// load 3046 into timer 1 to allow for an even
		TMR1L	= 0xEF;		// 500ms timer period.
		TMR1IF = 0;		// clear interrupt flag
		
		secInc ^= 1;
		
		if( secInc == 0 )
		{
			rtc.seconds++;
		}
		
		if( rtc.seconds == 60 )
		{
			rtc.seconds = 0;
			rtc.minutes++;
			TMR2ON = 0;
		}
		
		if( rtc.minutes == 60 )
		{
			rtc.minutes = 0;
			rtc.hours++;
		}
	}
	else if( RBIF )
	{
		RBIF = 0;	// clear interrupt flag
		awake = LIGHT_TRIGGER;
	}
}


/**
 * Sets the desired servo position
 *
 * @param pulseWidth Position to move servo to, in milliseconds.
 */
void setServo( int pulseWidth )
{
	if( servo < MIN_SERVO || servo > MAX_SERVO )
		servo = 0;
	else
	{
		TMR2ON = 1;
		servo = pulseWidth;
	}
}


/*!
 * \brief Delays for 500 milliseconds
 */
void delay_500ms( void )
{
	__delay_ms(100);
	__delay_ms(100);
	__delay_ms(100);
	__delay_ms(100);
	__delay_ms(100);
}
